/**
 * Copyright (c) 2011 eelpo.com
 * All rights reserved.
 */
package com.eelpo.common.xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.eelpo.common.util.BlankUtils;
import com.eelpo.common.xml.node.DefaultNodeCompare;
import com.eelpo.common.xml.node.DefaultNodeMerge;
import com.eelpo.common.xml.node.INodeCompare;
import com.eelpo.common.xml.node.INodeMerge;

/**
 * 判断对象是否为空
 * 
 * @author <a href="mailto:Brian@eelpo.com">汪洪州</a>
 * @since 1.0
 */
public class XMLMergeUtils {

	/**
	 * 合并指定的两个XML文件
	 * 
	 * @param source
	 *            源文件
	 * @param target
	 *            目标文件
	 * @param result
	 *            合并结果
	 * @return 返回是否合并成功
	 */
	public static boolean merge(String source, String target) {
		return merge(source, target, new StreamResult(new File(target)), new DefaultNodeCompare(), new DefaultNodeMerge());
	}

	/**
	 * 合并指定的两个XML文件
	 * 
	 * @param source
	 *            源文件
	 * @param target
	 *            目标文件
	 * @param result
	 *            合并结果
	 * @return 返回是否合并成功
	 */
	public static boolean merge(String source, String target, Result result) {
		return merge(source, target, result, new DefaultNodeCompare(), new DefaultNodeMerge());
	}

	/**
	 * 合并指定的两个XML文件
	 * 
	 * @param source
	 *            源文件
	 * @param target
	 *            目标文件
	 * @param result
	 *            合并结果
	 * @param nodeCompare
	 *            节点比较器
	 * @param nodeMerge
	 *            节点合并器
	 * @return 返回是否合并成功
	 */
	public static boolean merge(String source, String target, INodeCompare nodeCompare, INodeMerge nodeMerge) {
		return mergeFromFile(new File(source), new File(target), new StreamResult(new File(target)), nodeCompare, nodeMerge);
	}

	/**
	 * 合并指定的两个XML文件
	 * 
	 * @param source
	 *            源文件
	 * @param target
	 *            目标文件
	 * @param result
	 *            合并结果
	 * @param nodeCompare
	 *            节点比较器
	 * @param nodeMerge
	 *            节点合并器
	 * @return 返回是否合并成功
	 */
	public static boolean merge(String source, String target, Result result, INodeCompare nodeCompare, INodeMerge nodeMerge) {
		return mergeFromFile(new File(source), new File(target), result, nodeCompare, nodeMerge);
	}

	/**
	 * 批量合并指定的XML文件
	 * 
	 * @param files
	 *            需要合并的XMl文件
	 * @param result
	 *            合并结果
	 * @return 返回是否合并成功
	 */
	public static boolean merge(List<String> files, Result result) {
		return merge(files, result, new DefaultNodeCompare(), new DefaultNodeMerge());
	}

	/**
	 * 批量合并指定的XML文件
	 * 
	 * @param files
	 *            需要合并的XMl文件
	 * @param result
	 *            合并结果
	 * @param nodeCompare
	 *            节点比较器
	 * @param nodeMerge
	 *            节点合并器
	 * @return 返回是否合并成功
	 */
	public static boolean merge(List<String> files, Result result, INodeCompare nodeCompare, INodeMerge nodeMerge) {
		List<File> fileList = new ArrayList<File>();
		for (String file : files) {
			fileList.add(new File(file));
		}
		return mergeFromFiles(fileList, result, nodeCompare, nodeMerge);
	}

	/**
	 * 批量合并指定的XML文件
	 * 
	 * @param source
	 *            源文件
	 * @param target
	 *            目标文件
	 * @param result
	 *            合并结果
	 * @return 返回是否合并成功
	 */
	public static boolean mergeFromFile(File source, File target) {
		return mergeFromFile(source, target, new StreamResult(target), new DefaultNodeCompare(), new DefaultNodeMerge());
	}

	/**
	 * 批量合并指定的XML文件
	 * 
	 * @param source
	 *            源文件
	 * @param target
	 *            目标文件
	 * @param result
	 *            合并结果
	 * @return 返回是否合并成功
	 */
	public static boolean mergeFromFile(File source, File target, Result result) {
		return mergeFromFile(source, target, result, new DefaultNodeCompare(), new DefaultNodeMerge());
	}

	/**
	 * 批量合并指定的XML文件
	 * 
	 * @param source
	 *            源文件
	 * @param target
	 *            目标文件
	 * @param result
	 *            合并结果
	 * @param nodeCompare
	 *            节点比较器
	 * @param nodeMerge
	 *            节点合并器
	 * @return 返回是否合并成功
	 */
	public static boolean mergeFromFile(File source, File target, INodeCompare nodeCompare, INodeMerge nodeMerge) {
		try {
			return mergeFromInputStream(new FileInputStream(source), new FileInputStream(target), new StreamResult(target), nodeCompare, nodeMerge);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 批量合并指定的XML文件
	 * 
	 * @param source
	 *            源文件
	 * @param target
	 *            目标文件
	 * @param result
	 *            合并结果
	 * @param nodeCompare
	 *            节点比较器
	 * @param nodeMerge
	 *            节点合并器
	 * @return 返回是否合并成功
	 */
	public static boolean mergeFromFile(File source, File target, Result result, INodeCompare nodeCompare, INodeMerge nodeMerge) {
		try {
			return mergeFromInputStream(new FileInputStream(source), new FileInputStream(target), result, nodeCompare, nodeMerge);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 批量合并指定的XML文件
	 * 
	 * @param files
	 *            需要合并的XMl文件
	 * @param result
	 *            合并结果
	 * @return 返回是否合并成功
	 */
	public static boolean mergeFromFiles(List<File> files, Result result) {
		return mergeFromFiles(files, result, new DefaultNodeCompare(), new DefaultNodeMerge());
	}

	/**
	 * 批量合并指定的XML文件
	 * 
	 * @param files
	 *            需要合并的XMl文件
	 * @param result
	 *            合并结果
	 * @param nodeCompare
	 *            节点比较器
	 * @param nodeMerge
	 *            节点合并器
	 * @return 返回是否合并成功
	 */
	public static boolean mergeFromFiles(List<File> files, Result result, INodeCompare nodeCompare, INodeMerge nodeMerge) {
		try {
			List<InputStream> inputStreams = new ArrayList<InputStream>();
			for (File file : files) {
				inputStreams.add(new FileInputStream(file));
			}
			return mergeFromInputStreams(inputStreams, result, nodeCompare, nodeMerge);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 批量合并指定的XML文件
	 * 
	 * @param source
	 *            源文件
	 * @param target
	 *            目标文件
	 * @param result
	 *            合并结果
	 * @return 返回是否合并成功
	 */
	public static boolean mergeFromInputStream(InputStream source, InputStream target, Result result) {
		return mergeFromInputStream(source, target, result, new DefaultNodeCompare(), new DefaultNodeMerge());
	}

	/**
	 * 批量合并指定的XML文件
	 * 
	 * @param source
	 *            源文件
	 * @param target
	 *            目标文件
	 * @param result
	 *            合并结果
	 * @param nodeCompare
	 *            节点比较器
	 * @param nodeMerge
	 *            节点合并器
	 * @return 返回是否合并成功
	 */
	public static boolean mergeFromInputStream(InputStream source, InputStream target, Result result, INodeCompare nodeCompare, INodeMerge nodeMerge) {
		List<InputStream> inputStreams = new ArrayList<InputStream>(2);
		inputStreams.add(source);
		inputStreams.add(target);
		return mergeFromInputStreams(inputStreams, result, nodeCompare, nodeMerge);
	}

	/**
	 * 批量合并指定的XML文件
	 * 
	 * @param inputStreams
	 *            需要合并的XMl文件
	 * @param result
	 *            合并结果
	 * @return 返回是否合并成功
	 */
	public static boolean mergeFromInputStreams(List<InputStream> inputStreams, Result result) {
		return mergeFromInputStreams(inputStreams, result, new DefaultNodeCompare(), new DefaultNodeMerge());
	}

	/**
	 * 批量合并指定的XML文件
	 * 
	 * @param inputStreams
	 *            需要批量合并的XML文件
	 * @param result
	 *            合并结果
	 * @param nodeCompare
	 *            节点比较器
	 * @param nodeMerge
	 *            节点合并器
	 * @return 返回是否合并成功
	 */
	public static boolean mergeFromInputStreams(List<InputStream> inputStreams, Result result, INodeCompare nodeCompare, INodeMerge nodeMerge) {
		try {

			if (BlankUtils.isBlank(result)) {
				throw new IllegalArgumentException("结果输出参数不能为空!");
			}
			if (BlankUtils.isBlank(nodeCompare)) {
				throw new IllegalArgumentException("节点比较器不能为空!");
			}
			if (BlankUtils.isBlank(nodeMerge)) {
				throw new IllegalArgumentException("节点合并器不能为空!");
			}
			if (BlankUtils.isBlank(inputStreams) || inputStreams.size() < 2) {
				throw new IllegalArgumentException("输入参数错误，合并文件至少需要两个或以上!");
			}

			InputStream target = inputStreams.remove(0);
			DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			Document targetDocument = documentBuilder.parse(target);
			for (InputStream source : inputStreams) {
				Document sourceDocument = documentBuilder.parse(source);
				merge(targetDocument, sourceDocument.getDocumentElement(), targetDocument.getDocumentElement(), nodeCompare, nodeMerge);
			}

			DOMSource domSource = new DOMSource(targetDocument);
			Transformer transformer = TransformerFactory.newInstance().newTransformer();
			transformer.getOutputProperties().setProperty(OutputKeys.ENCODING, "UTF-8");
			transformer.transform(domSource, result);

			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 通过比较源的根元素下的子节点和目标元素下的子节点,合并不同的节点到目标document对象中
	 * 
	 * @param document
	 *            目标document对象
	 * @param source
	 *            源的根元素
	 * @param target
	 *            目标根元素
	 */
	protected static void merge(Document document, Element source, Element target, INodeCompare nodeCompare, INodeMerge nodeMerge) {
		NodeList childNodeList = source.getChildNodes();
		for (int i = 1, j = childNodeList.getLength(); i < j; i += 2) {
			Node sourceNode = childNodeList.item(i);
			if (!isChildNode(target, sourceNode, nodeCompare)) {
				nodeMerge.nodeMerge(document, target, sourceNode);
			}
		}
	}

	/**
	 * 判断源节点是否存在目标元素中
	 * 
	 * @param targetElement
	 *            目标元素
	 * @param sourceNode
	 *            源节点
	 * @return true表示存在，false表示不存在
	 */
	protected static boolean isChildNode(Element targetElement, Node sourceNode, INodeCompare nodeCompare) {
		NodeList childNodeList = targetElement.getChildNodes();
		for (int i = 1, j = childNodeList.getLength(); i < j; i += 2) {
			Node targetNode = childNodeList.item(i);
			if (nodeCompare.isEqualNode(sourceNode, targetNode)) {
				return true;
			}
		}
		return false;
	}
}